package com.ikingtech.platform.service.job.service;

import com.ikingtech.framework.sdk.context.exception.FrameworkException;
import com.ikingtech.framework.sdk.core.response.R;
import com.ikingtech.framework.sdk.enums.common.FrameworkServerFeedbackTypeEnum;
import com.ikingtech.framework.sdk.enums.job.JobStatusEnum;
import com.ikingtech.framework.sdk.enums.job.JobTypeEnum;
import com.ikingtech.framework.sdk.job.model.rpc.JobExecuteInfo;
import com.ikingtech.framework.sdk.job.scheduler.JobScheduler;
import com.ikingtech.framework.sdk.job.scheduler.ScheduledJob;
import com.ikingtech.framework.sdk.utils.Tools;
import com.ikingtech.framework.sdk.web.support.server.FrameworkServerProxy;
import com.ikingtech.platform.service.job.entity.JobDO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.ZoneId;

/**
 * @author tie yan
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class JobService {

    private final JobScheduler<JobDO> scheduler;

    private final JobRepository repository;

    /**
     * Provides feedback for a scheduled job.
     *
     * @param job The scheduled job to provide feedback for.
     */
    public void feedback(ScheduledJob<JobDO> job) {
        JobDO entity = job.getExtension();
        R<Object> result = FrameworkServerProxy.server().feedback(FrameworkServerFeedbackTypeEnum.JOB_FEEDBACK, this.createJobExecuteInfo(entity));
        if (!result.isSuccess()) {
            log.error("job feedback failed[{}]", result.getMsg());
        }

        if (JobTypeEnum.REPEATEDLY.name().equals(entity.getType())) {
            this.scheduler.schedule(this.createScheduledJob(entity));
        } else {
            entity.setStatus(JobStatusEnum.END.name());
            this.repository.updateById(entity);
        }
    }

    /**
     * Parses the execute time of a job based on its type.
     *
     * @param jobType   The type of the job.
     * @param corn      The cron expression for the job.
     * @param nextTime  The next execution time of the job.
     * @return          The parsed execute time in milliseconds.
     * @throws FrameworkException if the cron expression is invalid.
     */
    public long parseExecuteTime(String jobType, String corn, LocalDateTime nextTime) throws FrameworkException {
        if (JobTypeEnum.REPEATEDLY.name().equals(jobType)) {
            LocalDateTime date = Tools.Cron.next(corn);
            if (null == date) {
                throw new FrameworkException("invalidCronExpression");
            }
            return date.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
        } else {
            return nextTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
        }
    }

    /**
     * Creates a scheduled job based on the provided JobDO entity.
     *
     * @param entity The JobDO entity to create the scheduled job from.
     * @return       The created scheduled job.
     */
    public ScheduledJob<JobDO> createScheduledJob(JobDO entity) {
        ScheduledJob<JobDO> scheduledJob = new ScheduledJob<>();
        scheduledJob.setSerialNo(entity.getId());
        scheduledJob.setExecuteTime(this.parseExecuteTime(entity.getType(), entity.getCron(), entity.getNextTime()));
        scheduledJob.setExtension(entity);
        return scheduledJob;
    }

    /**
     * Creates a JobExecuteInfo object based on the provided JobDO entity.
     *
     * @param entity The JobDO entity to create the JobExecuteInfo from.
     * @return       The created JobExecuteInfo object.
     */
    public JobExecuteInfo createJobExecuteInfo(JobDO entity) {
        JobExecuteInfo jobExecuteInfo = new JobExecuteInfo();
        jobExecuteInfo.setSerialNo(entity.getId());
        jobExecuteInfo.setExecutorHandler(entity.getExecutorHandler());
        jobExecuteInfo.setExecutorClientId(entity.getExecutorClientId());
        jobExecuteInfo.setExecuteParam(entity.getParam());
        jobExecuteInfo.setTenantCode(entity.getTenantCode());
        return jobExecuteInfo;
    }
}
